
package net.lab0.fractal.gui;


import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;

import javax.swing.JComponent;

import net.lab0.fractal.classes.ViewCoordinates;
import net.lab0.fractal.fractals.DisplayableFractal;


/**
 * @author  116
 */
@SuppressWarnings("serial")
public class Fractal2dView
extends JComponent
implements MouseMotionListener, MouseWheelListener
{
    /**
	 * @uml.property  name="coords"
	 * @uml.associationEnd  
	 */
    private ViewCoordinates        coords;
    private double             zoomRatio;
    private double             zoom;
    /**
	 * @uml.property  name="fract"
	 * @uml.associationEnd  
	 */
    private DisplayableFractal fract;
    private Point              dragPointOrigin = null;
    private boolean            repaint         = true;
    private BufferedImage      lastRenderer    = null;
    
    public Fractal2dView(DisplayableFractal fract, ViewCoordinates coords)
    {
        this.fract = fract;
        this.coords = coords;
        this.zoom = 100;
        zoomRatio = Math.sqrt(2);
        addMouseMotionListener(this);
        addMouseWheelListener(this);
    }
    
    @Override
    public void mouseWheelMoved(MouseWheelEvent e)
    {
        double scaling = Math.pow(zoomRatio, -e.getWheelRotation());
        
        if (e.getWheelRotation() < 0)
        {
            double dx1 = (e.getPoint().x - getWidth() / 2) / zoom;
            double dy1 = (e.getPoint().y - getHeight() / 2) / zoom;
            
            double dx2 = (e.getPoint().x - getWidth() / 2) / (zoom * scaling);
            double dy2 = (e.getPoint().y - getHeight() / 2) / (zoom * scaling);
            
            coords.setxCenter(coords.getxCenter() - (dx2 - dx1));
            coords.setyCenter(coords.getyCenter() - (dy2 - dy1));
        }
        
        zoom *= scaling;
        repaint = true;
        repaint();
    }
    
    @Override
    public void mouseDragged(MouseEvent e)
    {
        coords.setxCenter(coords.getxCenter() - (e.getPoint().x - dragPointOrigin.x) / zoom);
        coords.setyCenter(coords.getyCenter() - (e.getPoint().y - dragPointOrigin.y) / zoom);
        dragPointOrigin = e.getPoint();
        
        repaint = true;
        repaint();
    }
    
    @Override
    public void mouseMoved(MouseEvent e)
    {
        dragPointOrigin = e.getPoint();
    }
    
    @Override
    protected void paintComponent(Graphics g)
    {
        if (repaint || lastRenderer == null || lastRenderer.getHeight() != getHeight() || lastRenderer.getWidth() != getWidth())
        {
            super.paintComponent(g);
            
            coords.setWidth(1 / zoom * getWidth());
            coords.setHeight(1 / zoom * getHeight());
            
            fract.setCoordinates(coords);
            lastRenderer = fract.renderImage(getWidth(), getHeight());
            repaint = false;
        }
        g.drawImage(lastRenderer, 0, 0, null);
    }

    public void forceRepaint()
    {
        repaint = true;
        repaint();
    }
    
}
